package edu.polsl.bio.str.manager.dao;

import edu.polsl.bio.str.common.constant.JobStatus;
import edu.polsl.bio.str.common.entity.Job;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;

/**
 * //todo class description
 * <p/>
 * Created on: Jun 14, 2011 5:40:53 PM <br/>
 * <a href="http://www.polsl.pl">www.polsl.pl</a>
 *
 * @author lukasz.olczak@polsl.pl
 */
public class JobDaoImpl extends HibernateDaoSupport implements JobDao {
  /**
   * slf4j Logger.
   */
  private static final Logger log = LoggerFactory.getLogger(JobDaoImpl.class.getName());

  private static final String UPDATE_QUERY = "update Job j set j.status=:status where j.node.id=:nodeId and"
                                             + " j.status=:oldStatus";

  @Override
  public Job create(Job job) {
    Serializable nodeId = getHibernateTemplate().save(job);
    return (Job) getHibernateTemplate().get(Job.class, nodeId);
  }

  @Override
  public long getCountOfWaitingJobs() {
    DetachedCriteria query = DetachedCriteria.forClass(Job.class).setProjection(Projections.rowCount())
        .add(Restrictions.eq("status", JobStatus.WAITING));

    return (Long) getHibernateTemplate().findByCriteria(query).get(0);
  }

  @Override
  public Job update(Job job) {
    getHibernateTemplate().update(job);
    return getById(job.getId());
  }

  @Override
  public Job getById(Long id) {
    return (Job) getHibernateTemplate().get(Job.class, id);
  }

  @Override
  public List<Job> getWaitingJobs(int count) {
    DetachedCriteria query = DetachedCriteria.forClass(Job.class).add(Restrictions.eq("status", JobStatus.WAITING))
        .setLockMode(LockMode.PESSIMISTIC_WRITE);

    return getHibernateTemplate().findByCriteria(query, 0, count);
  }

  @Override
  public int invalidateJobs(final Long nodeId) {
    return (Integer) getHibernateTemplate().execute(new HibernateCallback() {
      @Override
      public Object doInHibernate(Session session) throws HibernateException, SQLException {
        Query query = session.createQuery(UPDATE_QUERY);
        query.setParameter("status", JobStatus.WAITING);
        query.setParameter("nodeId", nodeId);
        query.setParameter("oldStatus", JobStatus.PROCESSING);

        return query.executeUpdate();
      }
    });
  }
}
